import {Lexer} from '../lexer/lexer';
import {Token,TokenConst, TokenType} from '../token/token';
import {LetStatement, Program, Statement, Identifier, ReturnStatement, IfStatement} from '../ast/ast';




export class Parser{
    private lexer: Lexer;
    private curToken: Token | null = null;
    private peekToken: Token | null = null ;
    private errors: string[];//当expectPeek中遇到不是预期类型的词法单元，目前代码只能返回null，而在parseProgram中会忽略这个null。因此若出现错误的输入，整条语句会悄无声息的被忽略。为了降低调试难度，需要在语法分析器中添加错误处理。

    // 连续调用nextToken，初始化curToken和peekToken
    // 初始化时，curToken为EOF，peekToken为第一个有效token
    // 调用nextToken后，curToken为peekToken，peekToken为下一个有效token
    // curToken和peekToken的行为与词法分析器中的两个指针positon和readPositon完全相同
    constructor(lexer: Lexer) {
        this.lexer = lexer;
        this.nextToken();
        this.nextToken();
        this.errors = [];
    }


    public nextToken(): void {
        this.curToken = this.peekToken;
        this.peekToken = this.lexer.nextToken();
        // console.log(`3. this.curToken.type=${this.curToken?.type, this.curToken?.literal}, this.peekToken=${this.peekToken?.type, this.peekToken?.literal}`);
    }

    public paserProgram(): Program{
        const program = new Program();
        // this.nextToken();
        while( this.curToken != null && this.curToken.type !== TokenConst.EOF){
            // console.log(`2. tok.type=${this.curToken.type}, ${this.curToken.literal}`);
            let statement: Statement | null = null;
            switch (this.curToken.type){
                case TokenConst.LET:
                    statement = this.parseLetStatement();
                    break;
                case TokenConst.RETURN:
                    statement = this.parseReturnStatement();
                    break;
                case TokenConst.IF:
                    statement = this.parseIfStatement();
                    break;
                default:
            }
            if(statement){
                program.statements.push(statement);
            }
            this.nextToken();
        }
        // console.log(`len: ${program.statements.length}`);

        return program;
    }

    parseLetStatement(): LetStatement | null {
        let statement = new LetStatement(this.curToken);
        this.nextToken();
        if (!this.curToken) {
            return null;
        }
        statement.name = new Identifier(this.curToken, this.curToken?.literal);
        if(!this.expectPeek(TokenConst.IDENT)){
            return null;
        }
        // console.error(`this.curToken: ${this.curToken.type}, ${this.curToken.literal}`);
        
        if(!this.expectPeek(TokenConst.ASSIGN)){
            return null;
        }
        //todo: 跳过对表达式的管理，直到遇见分号
        while(!this.curTokenIs(TokenConst.SEMICOLON)){
            this.nextToken();
        }
        return statement;
    }

    parseReturnStatement(): Statement | null{
        let statement = new ReturnStatement(this.curToken);
        if (!this.curToken) {
            return null;
        }
        //todo: 跳过对表达式的管理，直到遇见分号
        while(!this.curTokenIs(TokenConst.SEMICOLON)){
            this.nextToken();
        }
        return statement;
    }

    parseIfStatement(): Statement | null{
        let statement = new IfStatement(this.curToken);
        if (!this.curToken) {
            return null;
        }
        //todo: 跳过对表达式的管理，直到遇见分号
        while(!this.curTokenIs(TokenConst.SEMICOLON)){
            this.nextToken();
        }
        return statement;
    }

    curTokenIs(t :TokenType) : boolean{
        return this.curToken?.type === t
    } 
    
    peekTokenIs(t:TokenType) : boolean{
        return this.peekToken?.type === t
    }

    // 断言函数，主要目的是通过检查下一个词法单元的类型，确保词法单元顺序的正确性。
    expectPeek(t: TokenType) : boolean{
        if(this.curTokenIs(t)){
            this.nextToken();
            return true;
        }else{
            this.peekErrors(t);
            return false;
        }
    }

    getErrors(): string[]{
        return this.errors;
    }

    peekErrors(t: TokenType): void{
        const msg = `expect next token to be ${t}, but got ${this.peekToken?.type}`;
        // console.error(msg);
        this.errors.push(msg);
    }

}